/*-------------------------------------------------------------------------
 *
 * rewriteSearchCycle.c
 *		支持重写 SEARCH 和 CYCLE 子句。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/rewrite/rewriteSearchCycle.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_operator_d.h"
#include "catalog/pg_type_d.h"
#include "nodes/makefuncs.h"
#include "nodes/pg_list.h"
#include "nodes/parsenodes.h"
#include "nodes/primnodes.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSearchCycle.h"
#include "utils/fmgroids.h"



/*----------
 * 使用 SEARCH 或 CYCLE 子句重写 CTE
 *
 * 考虑一个像这样的 CTE
 *
 * WITH RECURSIVE ctename (col1, col2, col3) AS (
 *     query1
 *   UNION [ALL]
 *     SELECT trosl FROM ctename
 * )
 *
 * 使用搜索子句
 *
 * SEARCH BREADTH FIRST BY col1, col2 SET sqc
 *
 * CTE 被重写为
 *
 * WITH RECURSIVE ctename (col1, col2, col3, sqc) AS (
 *     SELECT col1, col2, col3,               -- 原始 WITH 列表
 *            ROW(0, col1, col2)              -- 搜索列的初始行
 *       FROM (query1) "*TLOCRN*" (col1, col2, col3)
 *   UNION [ALL]
 *     SELECT col1, col2, col3,               -- 与上面相同
 *            ROW(sqc.depth + 1, col1, col2)  -- 计数深度
 *       FROM (SELECT trosl, ctename.sqc FROM ctename) "*TROCRN*" (col1, col2, col3, sqc)
 * )
 *
 * （这不是完全合法的 SQL：sqc.depth 是旨在引用 sqc 的第一列，它具有行类型，但是字段名称在这里没有定义。在 SQL 中正确表示这一点会更复杂（SQL 标准实际上以更复杂的方式做到这一点），但是内部表示允许我们这样构造它。）
 *
 * 使用搜索子句
 *
 * SEARCH DEPTH FIRST BY col1, col2 SET sqc
 *
 * CTE 被重写为
 *
 * WITH RECURSIVE ctename (col1, col2, col3, sqc) AS (
 *     SELECT col1, col2, col3,               -- 原始 WITH 列表
 *            ARRAY[ROW(col1, col2)]          -- 搜索列的初始行
 *       FROM (query1) "*TLOCRN*" (col1, col2, col3)
 *   UNION [ALL]
 *     SELECT col1, col2, col3,               -- 与上面相同
 *            sqc || ARRAY[ROW(col1, col2)]   -- 记录看到的行
 *       FROM (SELECT trosl, ctename.sqc FROM ctename) "*TROCRN*" (col1, col2, col3, sqc)
 * )
 *
 * 使用循环子句
 *
 * CYCLE col1, col2 SET cmc TO 'Y' DEFAULT 'N' USING cpa
 *
 * （cmc = 循环标记列，cpa = 循环路径）CTE 被重写为
 *
 * WITH RECURSIVE ctename (col1, col2, col3, cmc, cpa) AS (
 *     SELECT col1, col2, col3,               -- 原始 WITH 列表
 *            'N',                            -- 循环标记默认值
 *            ARRAY[ROW(col1, col2)]          -- 循环列的初始行
 *       FROM (query1) "*TLOCRN*" (col1, col2, col3)
 *   UNION [ALL]
 *     SELECT col1, col2, col3,               -- 与上面相同
 *            CASE WHEN ROW(col1, col2) = ANY (ARRAY[cpa]) THEN 'Y' ELSE 'N' END,  -- 计算循环标记列
 *            cpa || ARRAY[ROW(col1, col2)]   -- 记录看到的行
 *       FROM (SELECT trosl, ctename.cmc, ctename.cpa FROM ctename) "*TROCRN*" (col1, col2, col3, cmc, cpa)
 *       WHERE cmc <> 'Y'
 * )
 *
 * 在右侧查询中计算循环标记列的表达式写作
 *
 * CASE WHEN ROW(col1, col2) IN (SELECT p.* FROM TABLE(cpa) p) THEN cmv ELSE cmd END
 *
 * 在 SQL 标准中，但在 PostgreSQL 中我们可以使用上面显示的标量数组操作符表达式。
 *
 * 此外，在某些情况下，上面显示的运算符我们实际上直接生成底层函数调用。
 *
 * 如果同时指定了搜索子句和循环子句，则搜索子句列会在循环子句列之前添加。
 */

/*
 * 从指定的列名创建 RowExpr，这些列名必须是 CTE 的输出列之一。
 */
static RowExpr * fc_make_path_rowexpr(const CommonTableExpr *fc_cte, const List *fc_col_list)
{
	RowExpr    *fc_rowexpr;
	ListCell   *fc_lc;

	fc_rowexpr = makeNode(RowExpr);
	fc_rowexpr->row_typeid = RECORDOID;
	fc_rowexpr->row_format = COERCE_IMPLICIT_CAST;
	fc_rowexpr->location = -1;

	foreach(fc_lc, fc_col_list)
	{
		char	   *fc_colname = strVal(lfirst(fc_lc));

		for (int fc_i = 0; fc_i < list_length(fc_cte->ctecolnames); fc_i++)
		{
			char	   *fc_colname2 = strVal(list_nth(fc_cte->ctecolnames, fc_i));

			if (strcmp(fc_colname, fc_colname2) == 0)
			{
				Var		   *fc_var;

				fc_var = makeVar(1, fc_i + 1,
							  list_nth_oid(fc_cte->ctecoltypes, fc_i),
							  list_nth_int(fc_cte->ctecoltypmods, fc_i),
							  list_nth_oid(fc_cte->ctecolcollations, fc_i),
							  0);
				fc_rowexpr->args = lappend(fc_rowexpr->args, fc_var);
				fc_rowexpr->colnames = lappend(fc_rowexpr->colnames, makeString(fc_colname));
				break;
			}
		}
	}

	return fc_rowexpr;
}

/*
 * 将 RowExpr 包装在 ArrayExpr 中，以用于初始搜索深度优先或循环行。
 */
static Expr * fc_make_path_initial_array(RowExpr *fc_rowexpr)
{
	ArrayExpr  *fc_arr;

	fc_arr = makeNode(ArrayExpr);
	fc_arr->array_typeid = RECORDARRAYOID;
	fc_arr->element_typeid = RECORDOID;
	fc_arr->location = -1;
	fc_arr->elements = list_make1(fc_rowexpr);

	return (Expr *) fc_arr;
}

/*
 * 创建一个数组连接表达式，类似于
 *
 * cpa || ARRAY[ROW(cols)]
 *
 * 其中 cpa 的 varattno 作为 path_varattno 提供。
 */
static Expr * fc_make_path_cat_expr(RowExpr *fc_rowexpr, AttrNumber fc_path_varattno)
{
	ArrayExpr  *fc_arr;
	FuncExpr   *fc_fexpr;

	fc_arr = makeNode(ArrayExpr);
	fc_arr->array_typeid = RECORDARRAYOID;
	fc_arr->element_typeid = RECORDOID;
	fc_arr->location = -1;
	fc_arr->elements = list_make1(fc_rowexpr);

	fc_fexpr = makeFuncExpr(F_ARRAY_CAT, RECORDARRAYOID,
						 list_make2(makeVar(1, fc_path_varattno, RECORDARRAYOID, -1, 0, 0),
									fc_arr),
						 InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);

	return (Expr *) fc_fexpr;
}

/*
 * 实际工作在这里进行。
 */
CommonTableExpr * rewriteSearchAndCycle(CommonTableExpr *fc_cte)
{
	Query	   *fc_ctequery;
	SetOperationStmt *fc_sos;
	int			fc_rti1,
				fc_rti2;
	RangeTblEntry *fc_rte1,
			   *fc_rte2,
			   *fc_newrte;
	Query	   *fc_newq1,
			   *fc_newq2;
	Query	   *fc_newsubquery;
	RangeTblRef *fc_rtr;
	Oid			fc_search_seq_type = InvalidOid;
	AttrNumber	fc_sqc_attno = InvalidAttrNumber;
	AttrNumber	fc_cmc_attno = InvalidAttrNumber;
	AttrNumber	fc_cpa_attno = InvalidAttrNumber;
	TargetEntry *fc_tle;
	RowExpr    *fc_cycle_col_rowexpr = NULL;
	RowExpr    *fc_search_col_rowexpr = NULL;
	List	   *fc_ewcl;
	int			fc_cte_rtindex = -1;

	Assert(fc_cte->search_clause || fc_cte->cycle_clause);

	fc_cte = copyObject(fc_cte);

	fc_ctequery = castNode(Query, fc_cte->ctequery);

	/*
	 * CTE 查询的顶层应该是一个 UNION。找到这两个
	 * 子查询。
	 */
	Assert(fc_ctequery->setOperations);
	fc_sos = castNode(SetOperationStmt, fc_ctequery->setOperations);
	Assert(fc_sos->op == SETOP_UNION);

	fc_rti1 = castNode(RangeTblRef, fc_sos->larg)->rtindex;
	fc_rti2 = castNode(RangeTblRef, fc_sos->rarg)->rtindex;

	fc_rte1 = rt_fetch(fc_rti1, fc_ctequery->rtable);
	fc_rte2 = rt_fetch(fc_rti2, fc_ctequery->rtable);

	Assert(fc_rte1->rtekind == RTE_SUBQUERY);
	Assert(fc_rte2->rtekind == RTE_SUBQUERY);

	/*
	 * 我们稍后需要这个几次。
	 */
	if (fc_cte->search_clause)
	{
		if (fc_cte->search_clause->search_breadth_first)
			fc_search_seq_type = RECORDOID;
		else
			fc_search_seq_type = RECORDARRAYOID;
	}

	/*
	 * CTE 列表中添加列的属性编号
	 */
	if (fc_cte->search_clause)
		fc_sqc_attno = list_length(fc_cte->ctecolnames) + 1;
	if (fc_cte->cycle_clause)
	{
		fc_cmc_attno = list_length(fc_cte->ctecolnames) + 1;
		fc_cpa_attno = list_length(fc_cte->ctecolnames) + 2;
		if (fc_cte->search_clause)
		{
			fc_cmc_attno++;
			fc_cpa_attno++;
		}
	}

	/*
	 * 创建新的左子查询
	 */
	fc_newq1 = makeNode(Query);
	fc_newq1->commandType = CMD_SELECT;
	fc_newq1->canSetTag = true;

	fc_newrte = makeNode(RangeTblEntry);
	fc_newrte->rtekind = RTE_SUBQUERY;
	fc_newrte->alias = makeAlias("*TLOCRN*", fc_cte->ctecolnames);
	fc_newrte->eref = fc_newrte->alias;
	fc_newsubquery = copyObject(fc_rte1->subquery);
	IncrementVarSublevelsUp((Node *) fc_newsubquery, 1, 1);
	fc_newrte->subquery = fc_newsubquery;
	fc_newrte->inFromCl = true;
	fc_newq1->rtable = list_make1(fc_newrte);

	fc_rtr = makeNode(RangeTblRef);
	fc_rtr->rtindex = 1;
	fc_newq1->jointree = makeFromExpr(list_make1(fc_rtr), NULL);

	/*
	 * 创建目标列表
	 */
	for (int fc_i = 0; fc_i < list_length(fc_cte->ctecolnames); fc_i++)
	{
		Var		   *fc_var;

		fc_var = makeVar(1, fc_i + 1,
					  list_nth_oid(fc_cte->ctecoltypes, fc_i),
					  list_nth_int(fc_cte->ctecoltypmods, fc_i),
					  list_nth_oid(fc_cte->ctecolcollations, fc_i),
					  0);
		fc_tle = makeTargetEntry((Expr *) fc_var, fc_i + 1, strVal(list_nth(fc_cte->ctecolnames, fc_i)), false);
		fc_tle->resorigtbl = list_nth_node(TargetEntry, fc_rte1->subquery->targetList, fc_i)->resorigtbl;
		fc_tle->resorigcol = list_nth_node(TargetEntry, fc_rte1->subquery->targetList, fc_i)->resorigcol;
		fc_newq1->targetList = lappend(fc_newq1->targetList, fc_tle);
	}

	if (fc_cte->search_clause)
	{
		Expr	   *fc_texpr;

		fc_search_col_rowexpr = fc_make_path_rowexpr(fc_cte, fc_cte->search_clause->search_col_list);
		if (fc_cte->search_clause->search_breadth_first)
		{
			fc_search_col_rowexpr->args = lcons(makeConst(INT8OID, -1, InvalidOid, sizeof(int64),
													   Int64GetDatum(0), false, FLOAT8PASSBYVAL),
											 fc_search_col_rowexpr->args);
			fc_search_col_rowexpr->colnames = lcons(makeString("*DEPTH*"), fc_search_col_rowexpr->colnames);
			fc_texpr = (Expr *) fc_search_col_rowexpr;
		}
		else
			fc_texpr = fc_make_path_initial_array(fc_search_col_rowexpr);
		fc_tle = makeTargetEntry(fc_texpr,
							  list_length(fc_newq1->targetList) + 1,
							  fc_cte->search_clause->search_seq_column,
							  false);
		fc_newq1->targetList = lappend(fc_newq1->targetList, fc_tle);
	}
	if (fc_cte->cycle_clause)
	{
		fc_tle = makeTargetEntry((Expr *) fc_cte->cycle_clause->cycle_mark_default,
							  list_length(fc_newq1->targetList) + 1,
							  fc_cte->cycle_clause->cycle_mark_column,
							  false);
		fc_newq1->targetList = lappend(fc_newq1->targetList, fc_tle);
		fc_cycle_col_rowexpr = fc_make_path_rowexpr(fc_cte, fc_cte->cycle_clause->cycle_col_list);
		fc_tle = makeTargetEntry(fc_make_path_initial_array(fc_cycle_col_rowexpr),
							  list_length(fc_newq1->targetList) + 1,
							  fc_cte->cycle_clause->cycle_path_column,
							  false);
		fc_newq1->targetList = lappend(fc_newq1->targetList, fc_tle);
	}

	fc_rte1->subquery = fc_newq1;

	if (fc_cte->search_clause)
	{
		fc_rte1->eref->colnames = lappend(fc_rte1->eref->colnames, makeString(fc_cte->search_clause->search_seq_column));
	}
	if (fc_cte->cycle_clause)
	{
		fc_rte1->eref->colnames = lappend(fc_rte1->eref->colnames, makeString(fc_cte->cycle_clause->cycle_mark_column));
		fc_rte1->eref->colnames = lappend(fc_rte1->eref->colnames, makeString(fc_cte->cycle_clause->cycle_path_column));
	}

	/*
	 * 创建新的右子查询
	 */
	fc_newq2 = makeNode(Query);
	fc_newq2->commandType = CMD_SELECT;
	fc_newq2->canSetTag = true;

	fc_newrte = makeNode(RangeTblEntry);
	fc_newrte->rtekind = RTE_SUBQUERY;
	fc_ewcl = copyObject(fc_cte->ctecolnames);
	if (fc_cte->search_clause)
	{
		fc_ewcl = lappend(fc_ewcl, makeString(fc_cte->search_clause->search_seq_column));
	}
	if (fc_cte->cycle_clause)
	{
		fc_ewcl = lappend(fc_ewcl, makeString(fc_cte->cycle_clause->cycle_mark_column));
		fc_ewcl = lappend(fc_ewcl, makeString(fc_cte->cycle_clause->cycle_path_column));
	}
	fc_newrte->alias = makeAlias("*TROCRN*", fc_ewcl);
	fc_newrte->eref = fc_newrte->alias;

	/*
	 * 在右侧 UNION 子查询的范围表中找到对递归 CTE 的引用。我们希望它位于 UNION 子查询的两个级别上（必须检查以避免被具有相同 CTE 名称的子 WITH 所欺骗）。此引用不会超过一个，因为解析器会拒绝那样的情况（参见 parse_cte.c 中的 checkWellFormedRecursion()）。但是，解析器并不一定要求引用出现在 UNION 子查询的最上层范围表中，因此我们可能根本找不到它。这是一个暂时未实现的情况。
	 */
	for (int fc_rti = 1; fc_rti <= list_length(fc_rte2->subquery->rtable); fc_rti++)
	{
		RangeTblEntry *fc_e = rt_fetch(fc_rti, fc_rte2->subquery->rtable);

		if (fc_e->rtekind == RTE_CTE &&
			strcmp(fc_cte->ctename, fc_e->ctename) == 0 &&
			fc_e->ctelevelsup == 2)
		{
			fc_cte_rtindex = fc_rti;
			break;
		}
	}
	if (fc_cte_rtindex <= 0)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("with a SEARCH or CYCLE clause, the recursive reference to WITH query \"%s\" must be at the top level of its right-hand SELECT",
						fc_cte->ctename)));

	fc_newsubquery = copyObject(fc_rte2->subquery);
	IncrementVarSublevelsUp((Node *) fc_newsubquery, 1, 1);

	/*
	 * 将额外的列添加到右子查询子查询的目标列表中
	 */
	if (fc_cte->search_clause)
	{
		Var		   *fc_var;

		/* ctename.sqc */
		fc_var = makeVar(fc_cte_rtindex, fc_sqc_attno,
					  fc_search_seq_type, -1, InvalidOid, 0);
		fc_tle = makeTargetEntry((Expr *) fc_var,
							  list_length(fc_newsubquery->targetList) + 1,
							  fc_cte->search_clause->search_seq_column,
							  false);
		fc_newsubquery->targetList = lappend(fc_newsubquery->targetList, fc_tle);
	}
	if (fc_cte->cycle_clause)
	{
		Var		   *fc_var;

		/* ctename.cmc */
		fc_var = makeVar(fc_cte_rtindex, fc_cmc_attno,
					  fc_cte->cycle_clause->cycle_mark_type,
					  fc_cte->cycle_clause->cycle_mark_typmod,
					  fc_cte->cycle_clause->cycle_mark_collation, 0);
		fc_tle = makeTargetEntry((Expr *) fc_var,
							  list_length(fc_newsubquery->targetList) + 1,
							  fc_cte->cycle_clause->cycle_mark_column,
							  false);
		fc_newsubquery->targetList = lappend(fc_newsubquery->targetList, fc_tle);

		/* ctename.cpa */
		fc_var = makeVar(fc_cte_rtindex, fc_cpa_attno,
					  RECORDARRAYOID, -1, InvalidOid, 0);
		fc_tle = makeTargetEntry((Expr *) fc_var,
							  list_length(fc_newsubquery->targetList) + 1,
							  fc_cte->cycle_clause->cycle_path_column,
							  false);
		fc_newsubquery->targetList = lappend(fc_newsubquery->targetList, fc_tle);
	}

	fc_newrte->subquery = fc_newsubquery;
	fc_newrte->inFromCl = true;
	fc_newq2->rtable = list_make1(fc_newrte);

	fc_rtr = makeNode(RangeTblRef);
	fc_rtr->rtindex = 1;

	if (fc_cte->cycle_clause)
	{
		Expr	   *fc_expr;

		/*
		 * 添加 cmc <> cmv 条件
		 */
		fc_expr = make_opclause(fc_cte->cycle_clause->cycle_mark_neop, BOOLOID, false,
							 (Expr *) makeVar(1, fc_cmc_attno,
											  fc_cte->cycle_clause->cycle_mark_type,
											  fc_cte->cycle_clause->cycle_mark_typmod,
											  fc_cte->cycle_clause->cycle_mark_collation, 0),
							 (Expr *) fc_cte->cycle_clause->cycle_mark_value,
							 InvalidOid,
							 fc_cte->cycle_clause->cycle_mark_collation);

		fc_newq2->jointree = makeFromExpr(list_make1(fc_rtr), (Node *) fc_expr);
	}
	else
		fc_newq2->jointree = makeFromExpr(list_make1(fc_rtr), NULL);

	/*
	 * 创建目标列表
	 */
	for (int fc_i = 0; fc_i < list_length(fc_cte->ctecolnames); fc_i++)
	{
		Var		   *fc_var;

		fc_var = makeVar(1, fc_i + 1,
					  list_nth_oid(fc_cte->ctecoltypes, fc_i),
					  list_nth_int(fc_cte->ctecoltypmods, fc_i),
					  list_nth_oid(fc_cte->ctecolcollations, fc_i),
					  0);
		fc_tle = makeTargetEntry((Expr *) fc_var, fc_i + 1, strVal(list_nth(fc_cte->ctecolnames, fc_i)), false);
		fc_tle->resorigtbl = list_nth_node(TargetEntry, fc_rte2->subquery->targetList, fc_i)->resorigtbl;
		fc_tle->resorigcol = list_nth_node(TargetEntry, fc_rte2->subquery->targetList, fc_i)->resorigcol;
		fc_newq2->targetList = lappend(fc_newq2->targetList, fc_tle);
	}

	if (fc_cte->search_clause)
	{
		Expr	   *fc_texpr;

		if (fc_cte->search_clause->search_breadth_first)
		{
			FieldSelect *fc_fs;
			FuncExpr   *fc_fexpr;

			/*
			 * ROW(sqc.depth + 1, cols)
			 */

			fc_search_col_rowexpr = copyObject(fc_search_col_rowexpr);

			fc_fs = makeNode(FieldSelect);
			fc_fs->arg = (Expr *) makeVar(1, fc_sqc_attno, RECORDOID, -1, 0, 0);
			fc_fs->fieldnum = 1;
			fc_fs->resulttype = INT8OID;
			fc_fs->resulttypmod = -1;

			fc_fexpr = makeFuncExpr(F_INT8INC, INT8OID, list_make1(fc_fs), InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);

			lfirst(list_head(fc_search_col_rowexpr->args)) = fc_fexpr;

			fc_texpr = (Expr *) fc_search_col_rowexpr;
		}
		else
		{
			/*
			 * sqc || ARRAY[ROW(cols)]
			 */
			fc_texpr = fc_make_path_cat_expr(fc_search_col_rowexpr, fc_sqc_attno);
		}
		fc_tle = makeTargetEntry(fc_texpr,
							  list_length(fc_newq2->targetList) + 1,
							  fc_cte->search_clause->search_seq_column,
							  false);
		fc_newq2->targetList = lappend(fc_newq2->targetList, fc_tle);
	}

	if (fc_cte->cycle_clause)
	{
		ScalarArrayOpExpr *fc_saoe;
		CaseExpr   *fc_caseexpr;
		CaseWhen   *fc_casewhen;

		/*
		 * CASE WHEN ROW(cols) = ANY (ARRAY[cpa]) THEN cmv ELSE cmd END
		 */

		fc_saoe = makeNode(ScalarArrayOpExpr);
		fc_saoe->location = -1;
		fc_saoe->opno = RECORD_EQ_OP;
		fc_saoe->useOr = true;
		fc_saoe->args = list_make2(fc_cycle_col_rowexpr,
								makeVar(1, fc_cpa_attno, RECORDARRAYOID, -1, 0, 0));

		fc_caseexpr = makeNode(CaseExpr);
		fc_caseexpr->location = -1;
		fc_caseexpr->casetype = fc_cte->cycle_clause->cycle_mark_type;
		fc_caseexpr->casecollid = fc_cte->cycle_clause->cycle_mark_collation;
		fc_casewhen = makeNode(CaseWhen);
		fc_casewhen->location = -1;
		fc_casewhen->expr = (Expr *) fc_saoe;
		fc_casewhen->result = (Expr *) fc_cte->cycle_clause->cycle_mark_value;
		fc_caseexpr->args = list_make1(fc_casewhen);
		fc_caseexpr->defresult = (Expr *) fc_cte->cycle_clause->cycle_mark_default;

		fc_tle = makeTargetEntry((Expr *) fc_caseexpr,
							  list_length(fc_newq2->targetList) + 1,
							  fc_cte->cycle_clause->cycle_mark_column,
							  false);
		fc_newq2->targetList = lappend(fc_newq2->targetList, fc_tle);

		/*
		 * cpa || ARRAY[ROW(cols)]
		 */
		fc_tle = makeTargetEntry(fc_make_path_cat_expr(fc_cycle_col_rowexpr, fc_cpa_attno),
							  list_length(fc_newq2->targetList) + 1,
							  fc_cte->cycle_clause->cycle_path_column,
							  false);
		fc_newq2->targetList = lappend(fc_newq2->targetList, fc_tle);
	}

	fc_rte2->subquery = fc_newq2;

	if (fc_cte->search_clause)
	{
		fc_rte2->eref->colnames = lappend(fc_rte2->eref->colnames, makeString(fc_cte->search_clause->search_seq_column));
	}
	if (fc_cte->cycle_clause)
	{
		fc_rte2->eref->colnames = lappend(fc_rte2->eref->colnames, makeString(fc_cte->cycle_clause->cycle_mark_column));
		fc_rte2->eref->colnames = lappend(fc_rte2->eref->colnames, makeString(fc_cte->cycle_clause->cycle_path_column));
	}

	/*
	 * 将额外列添加到 SetOperationStmt
	 */
	if (fc_cte->search_clause)
	{
		fc_sos->colTypes = lappend_oid(fc_sos->colTypes, fc_search_seq_type);
		fc_sos->colTypmods = lappend_int(fc_sos->colTypmods, -1);
		fc_sos->colCollations = lappend_oid(fc_sos->colCollations, InvalidOid);
		if (!fc_sos->all)
			fc_sos->groupClauses = lappend(fc_sos->groupClauses,
										makeSortGroupClauseForSetOp(fc_search_seq_type, true));
	}
	if (fc_cte->cycle_clause)
	{
		fc_sos->colTypes = lappend_oid(fc_sos->colTypes, fc_cte->cycle_clause->cycle_mark_type);
		fc_sos->colTypmods = lappend_int(fc_sos->colTypmods, fc_cte->cycle_clause->cycle_mark_typmod);
		fc_sos->colCollations = lappend_oid(fc_sos->colCollations, fc_cte->cycle_clause->cycle_mark_collation);
		if (!fc_sos->all)
			fc_sos->groupClauses = lappend(fc_sos->groupClauses,
										makeSortGroupClauseForSetOp(fc_cte->cycle_clause->cycle_mark_type, true));

		fc_sos->colTypes = lappend_oid(fc_sos->colTypes, RECORDARRAYOID);
		fc_sos->colTypmods = lappend_int(fc_sos->colTypmods, -1);
		fc_sos->colCollations = lappend_oid(fc_sos->colCollations, InvalidOid);
		if (!fc_sos->all)
			fc_sos->groupClauses = lappend(fc_sos->groupClauses,
										makeSortGroupClauseForSetOp(RECORDARRAYOID, true));
	}

	/*
	 * 将额外列添加到 CTE 查询的目标列表
	 */
	if (fc_cte->search_clause)
	{
		fc_ctequery->targetList = lappend(fc_ctequery->targetList,
									   makeTargetEntry((Expr *) makeVar(1, fc_sqc_attno,
																		fc_search_seq_type, -1, InvalidOid, 0),
													   list_length(fc_ctequery->targetList) + 1,
													   fc_cte->search_clause->search_seq_column,
													   false));
	}
	if (fc_cte->cycle_clause)
	{
		fc_ctequery->targetList = lappend(fc_ctequery->targetList,
									   makeTargetEntry((Expr *) makeVar(1, fc_cmc_attno,
																		fc_cte->cycle_clause->cycle_mark_type,
																		fc_cte->cycle_clause->cycle_mark_typmod,
																		fc_cte->cycle_clause->cycle_mark_collation, 0),
													   list_length(fc_ctequery->targetList) + 1,
													   fc_cte->cycle_clause->cycle_mark_column,
													   false));
		fc_ctequery->targetList = lappend(fc_ctequery->targetList,
									   makeTargetEntry((Expr *) makeVar(1, fc_cpa_attno,
																		RECORDARRAYOID, -1, InvalidOid, 0),
													   list_length(fc_ctequery->targetList) + 1,
													   fc_cte->cycle_clause->cycle_path_column,
													   false));
	}

	/*
	 * 将额外列添加到 CTE 的输出列
	 */
	fc_cte->ctecolnames = fc_ewcl;
	if (fc_cte->search_clause)
	{
		fc_cte->ctecoltypes = lappend_oid(fc_cte->ctecoltypes, fc_search_seq_type);
		fc_cte->ctecoltypmods = lappend_int(fc_cte->ctecoltypmods, -1);
		fc_cte->ctecolcollations = lappend_oid(fc_cte->ctecolcollations, InvalidOid);
	}
	if (fc_cte->cycle_clause)
	{
		fc_cte->ctecoltypes = lappend_oid(fc_cte->ctecoltypes, fc_cte->cycle_clause->cycle_mark_type);
		fc_cte->ctecoltypmods = lappend_int(fc_cte->ctecoltypmods, fc_cte->cycle_clause->cycle_mark_typmod);
		fc_cte->ctecolcollations = lappend_oid(fc_cte->ctecolcollations, fc_cte->cycle_clause->cycle_mark_collation);

		fc_cte->ctecoltypes = lappend_oid(fc_cte->ctecoltypes, RECORDARRAYOID);
		fc_cte->ctecoltypmods = lappend_int(fc_cte->ctecoltypmods, -1);
		fc_cte->ctecolcollations = lappend_oid(fc_cte->ctecolcollations, InvalidOid);
	}

	return fc_cte;
}
